#ifndef _STRINGPIECE_H_
#define _STRINGPIECE_H_

#include <cassert>
#include <cstring>
#include <iosfwd> // for ostream forward-declaration
#include <string>
// using std::string;

namespace netlib {

// For passing C-style string argument to a function.
class StringArg {
public:
	StringArg(const char* str) : str_(str) {}                // NOLINT
	StringArg(const std::string& str) : str_(str.c_str()) {} // NOLINT

	const char* CStr() const { return str_; }

private:
	const char* str_;
};

class StringPiece {
public:
	// We provide non-explicit singleton constructors so users
	// can pass in a "const char*" or a "string" wherever a
	// "StringPiece" is expected.
	StringPiece() : ptr_(nullptr), length_(0) {}
	explicit StringPiece(const char* str) : ptr_(str), length_(static_cast<int>(strlen(ptr_))) {}
	explicit StringPiece(const unsigned char* str) :
	    ptr_(reinterpret_cast<const char*>(str)), length_(static_cast<int>(strlen(ptr_))) {}
	explicit StringPiece(const std::string& str) :
	    ptr_(str.data()), length_(static_cast<int>(str.size())) {}
	StringPiece(const char* offset, int len) : ptr_(offset), length_(len) {}

	// data() may return a pointer to a buffer with embedded
	// NULs, and the returned buffer may or may not be null
	// terminated. Therefore it is typically a mistake to pass
	// data() to a routine that expects a NUL terminated string.
	// Use "as_string().c_str()" if you really need to do this.
	// Or better yet, change your routine so it does not rely on
	// NUL termination.
	const char* GetData() const { return ptr_; }
	int Size() const { return length_; }
	bool Empty() const { return length_ == 0; }
	const char* Begin() const { return ptr_; }
	const char* End() const { return ptr_ + length_; }

	void Clear() {
		ptr_ = nullptr;
		length_ = 0;
	}
	void Set(const char* buffer, int len) {
		ptr_ = buffer;
		length_ = len;
	}
	void Set(const char* str) {
		ptr_ = str;
		length_ = static_cast<int>(strlen(str));
	}
	void Set(const void* buffer, int len) {
		ptr_ = reinterpret_cast<const char*>(buffer);
		length_ = len;
	}

	char operator[](int i) const {
		assert(i >= 0);
		return ptr_[i];
	}

	void RemovePrefix(int n) {
		ptr_ += n;
		length_ -= n;
	}

	void RemoveSuffix(int n) { length_ -= n; }

	bool operator==(const StringPiece& x) const {
		return ((length_ == x.length_) && (memcmp(ptr_, x.ptr_, length_) == 0));
	}
	bool operator!=(const StringPiece& x) const { return !(*this == x); }

#define STRINGPIECE_BINARY_PREDICATE(cmp, auxcmp)                                \
	bool operator cmp(const StringPiece& x) const {                              \
		int r = memcmp(ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_); \
		return ((r auxcmp 0) || ((r == 0) && (length_ cmp x.length_)));          \
	}
	STRINGPIECE_BINARY_PREDICATE(<, <);
	STRINGPIECE_BINARY_PREDICATE(<=, <);
	STRINGPIECE_BINARY_PREDICATE(>=, >);
	STRINGPIECE_BINARY_PREDICATE(>, >);
#undef STRINGPIECE_BINARY_PREDICATE

	int Compare(const StringPiece& x) const {
		int r = memcmp(ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_);
		if (r == 0) {
			if (length_ < x.length_) {
				r = -1;
			} else if (length_ > x.length_) {
				r = +1;
			}
		}
		return r;
	}

	// std::string AsString() const { return std::string(GetData(), Size()); }
	std::string AsString() const { return {GetData(), static_cast<std::size_t>(Size())}; }

	void CopyToString(std::string* target) const { target->assign(ptr_, length_); }

	// Does "this" start with "x"
	bool StartsWith(const StringPiece& x) const {
		return ((length_ >= x.length_) && (memcmp(ptr_, x.ptr_, x.length_) == 0));
	}

private:
	const char* ptr_;
	int length_;
};

} // namespace netlib

// ------------------------------------------------------------------
// Functions used to create STL containers that use StringPiece
//  Remember that a StringPiece's lifetime had better be less
//  than that of the underlying string or char*.  If it is not,
//  then you cannot safely store a StringPiece into an STL
//  container
// ------------------------------------------------------------------

#ifdef HAVE_TYPE_TRAITS
// This makes vector<StringPiece> really fast for some STL
// implementations
template <> struct __type_traits<muduo::StringPiece> {
	typedef __true_type has_trivial_default_constructor;
	typedef __true_type has_trivial_copy_constructor;
	typedef __true_type has_trivial_assignment_operator;
	typedef __true_type has_trivial_destructor;
	typedef __true_type is_POD_type;
};
#endif

// allow StringPiece to be logged
std::ostream& operator<<(std::ostream& o, const netlib::StringPiece& piece);

#endif // MUDUO_BASE_STRINGPIECE_H
